import time
import os
import logging
import re
import threading
from datetime import datetime, timedelta
import numpy as np

# 第三方库导入
import pandas as pd
import pandas_market_calendars as mcal
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException, WebDriverException, StaleElementReferenceException
from bs4 import BeautifulSoup
from webdriver_manager.chrome import ChromeDriverManager
import random

# GUI相关导入
import tkinter as tk
from tkinter import ttk, messagebox, scrolledtext
from tkinter import filedialog
from tkinter import simpledialog

import matplotlib.pyplot as plt
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from concurrent.futures import ThreadPoolExecutor, as_completed

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.StreamHandler(),
        logging.FileHandler("analyzer_debug.log", mode='a', encoding='utf-8')
    ]
)


class CFFEXShortPositionAnalyzer:
    """中国金融期货交易所多合约净空单数据分析器"""
    
    # 合约最早日期
    INSTRUMENT_START_DATES = {
        "IF": datetime(2010, 4, 16),   # 沪深300股指期货
        "IH": datetime(2015, 4, 16),   # 上证50股指期货
        "IC": datetime(2015, 4, 16),   # 中证500股指期货
        "IM": datetime(2022, 7, 22)    # 中证1000股指期货
    }
    
    def __init__(self, save_dir="cffex_data", headless=True, max_timeout=45):
        self.base_url = "http://www.cffex.com.cn"
        self.ccpm_url_template = f"{self.base_url}/ccpm/?trading_date={{date}}"
        self.save_dir = save_dir
        self.instruments = {"IF": "沪深300股指期货", "IC": "中证500股指期货", 
                           "IM": "中证1000股指期货", "IH": "上证50股指期货"}
        self.headless = headless
        self.max_timeout = max_timeout
        self.calendar = mcal.get_calendar('XSHG')
        
        if not os.path.exists(save_dir):
            os.makedirs(save_dir, exist_ok=True)
            logging.info(f"创建根数据保存目录: {save_dir}")
        
        # 配置Selenium
        self._setup_browser()
        logging.info("Selenium WebDriver 初始化完成")
    
    def _setup_browser(self):
        """浏览器配置 - 增强反爬和稳定性"""
        chrome_options = Options()
        if self.headless:
            chrome_options.add_argument("--headless=new")
        
        # 基础配置
        chrome_options.add_argument("--disable-gpu")
        chrome_options.add_argument("--window-size=1920,1080")
        chrome_options.add_argument("--disable-blink-features=AutomationControlled")
        chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
        chrome_options.add_experimental_option("useAutomationExtension", False)
        
        # 用户代理随机化
        chrome_options.add_argument(f"user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{random.randint(100, 130)}.0.0.0 Safari/537.36")
        
        # 增强反爬配置
        chrome_options.add_experimental_option("prefs", {
            "profile.default_content_settings.popups": 0,
            "download.default_directory": self.save_dir,
            "directory_upgrade": True
        })
        
        # SSL兼容和安全设置
        chrome_options.add_argument("--ignore-certificate-errors")
        chrome_options.add_argument("--allow-insecure-localhost")
        chrome_options.add_argument("--disable-web-security")
        chrome_options.add_argument("--no-sandbox")
        
        # 隐藏WebDriver特征
        self.driver = webdriver.Chrome(
            service=Service(ChromeDriverManager().install()),
            options=chrome_options
        )
        
        # 执行CDP命令隐藏WebDriver特征
        self.driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
            "source": """
                Object.defineProperty(navigator, 'webdriver', { get: () => undefined });
                window.navigator.chrome = { runtime: {} };
                Object.defineProperty(navigator, 'languages', { get: () => ['zh-CN', 'zh', 'en'] });
                Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3, 4, 5] });
            """
        })
        
        # 设置较长的隐式等待
        self.driver.implicitly_wait(20)
    
    def get_trading_date(self, date=None):
        """获取有效交易日 - 增强错误处理"""
        if not date:
            date = datetime.now().strftime("%Y%m%d")
        
        try:
            date_obj = datetime.strptime(date, "%Y%m%d")
        except ValueError:
            logging.error(f"日期格式错误: {date}，使用当前日期")
            date = datetime.now().strftime("%Y%m%d")
            date_obj = datetime.now()
        
        # 增加重试机制获取前一个交易日
        prev_trading_day = self._get_previous_trading_day_with_retry(date_obj)
        formatted_date = prev_trading_day.strftime("%Y%m%d")
        logging.info(f"计算的交易日: {formatted_date}")
        return formatted_date
    
    def _get_previous_trading_day_with_retry(self, date_obj, max_attempts=15):
        """获取前一个交易日 - 带重试机制"""
        if not self._is_holiday(date_obj):
            return date_obj
        
        current_date = date_obj
        for attempt in range(max_attempts):
            current_date -= timedelta(days=1)
            if not self._is_holiday(current_date):
                return current_date
            logging.info(f"尝试第{attempt+1}次查找有效交易日...")
        
        raise ValueError(f"无法找到近{max_attempts}天内的有效交易日")
    
    def _is_holiday(self, date_obj):
        """判断是否为非交易日（周末或节假日）"""
        if date_obj.weekday() >= 5:  # 周末（5=周六, 6=周日）
            return True
        
        # 使用pandas_market_calendars检查是否为交易所假期
        schedule = self.calendar.schedule(
            start_date=date_obj.strftime("%Y-%m-%d"),
            end_date=date_obj.strftime("%Y-%m-%d")
        )
        return schedule.empty
    
    def load_dynamic_page(self, date, instrument_code):
        """加载指定合约的页面并执行交互操作"""
        base_url = "http://www.cffex.com.cn/ccpm/"  # 基础URL，不再包含日期参数
        logging.info(f"加载页面: {base_url}，合约: {instrument_code}，日期: {date}")

        # 日期格式转换（假设网页要求YYYY-MM-DD格式）
        date_obj = datetime.strptime(date, "%Y%m%d")
        formatted_date = date_obj.strftime("%Y-%m-%d")
        
        # 页面加载重试机制
        for attempt in range(3):
            try:
                self.driver.get(base_url)
                time.sleep(random.uniform(3, 6))  # 随机等待，模拟人工操作
                break
            except WebDriverException as e:
                logging.error(f"页面加载失败 (尝试{attempt+1}/3): {e}")
                if attempt == 2:
                    raise
        
        try:
            # 等待日期输入框加载
            date_input = WebDriverWait(self.driver, self.max_timeout).until(
                EC.presence_of_element_located((By.ID, "actualDate"))
            )
            
            # 清空并输入日期（带防卡顿处理）
            self.driver.execute_script("arguments[0].value = '';", date_input)
            time.sleep(random.uniform(0.5, 1))
            date_input.send_keys(formatted_date)
            logging.info(f"已输入日期: {formatted_date}")
            time.sleep(random.uniform(1, 2))
            
            # 等待期货类型选择按钮加载
            WebDriverWait(self.driver, self.max_timeout).until(
                EC.presence_of_element_located((By.CSS_SELECTOR, "input[value='期货']"))
            )   
            
            # 选择期货类型
            radio = self._safe_find_element(By.CSS_SELECTOR, "input[value='期货']")
            self._highlight_and_click(radio)
            logging.info("已选择'期货'类型")
            time.sleep(random.uniform(1.5, 3))
            
            # 选择合约
            select_element = self._safe_find_element(By.ID, "selectSec")
            options = select_element.find_elements(By.TAG_NAME, "option")
            for option in options:
                if option.get_attribute("value") == instrument_code:
                    option.click()
                    logging.info(f"已选择合约: {instrument_code}")
                    break
            time.sleep(random.uniform(1.5, 3))
            
            # 点击查询按钮
            query_button = self._wait_and_find_element(By.CSS_SELECTOR, ".btn-query", self.max_timeout)
            self._highlight_and_click(query_button)
            logging.info("已点击查询按钮，等待数据加载")
            
            # 等待表格加载 - 使用更通用的表格选择器
            table_loaded = EC.presence_of_element_located((By.CSS_SELECTOR, "table"))
            WebDriverWait(self.driver, self.max_timeout).until(table_loaded)
            logging.info("表格数据加载完成")
            
        except TimeoutException:
            logging.error("页面交互超时")
            self._save_page_source(date, instrument_code, "timeout")
            # 尝试使用备用选择器
            self._try_alternative_selectors(date, instrument_code)
            raise
        
        # 保存完整页面源码
        self._save_page_source(date, instrument_code, "full")
        return BeautifulSoup(self.driver.page_source, 'html.parser')
    
    def _safe_find_element(self, by, value, timeout=15):
        """安全查找元素 - 带超时处理"""
        try:
            return WebDriverWait(self.driver, timeout).until(
                EC.presence_of_element_located((by, value))
            )
        except TimeoutException:
            logging.error(f"元素查找超时: {by}={value}")
            raise
    
    def _wait_and_find_element(self, by, value, timeout=15):
        """等待并查找可点击元素"""
        try:
            return WebDriverWait(self.driver, timeout).until(
                EC.element_to_be_clickable((by, value))
            )
        except TimeoutException:
            logging.error(f"可点击元素查找超极: {by}={value}")
            raise
    
    def _try_alternative_selectors(self, date, instrument_code):
        """尝试备用选择器 - 应对页面结构变化"""
        logging.info("尝试使用备用选择器...")
        alternative_selectors = [
            (By.XPATH, "//input[@value='期货']"),
            (By.ID, "selectSec"),
            (By.XPATH, f"//option[@value='{instrument_code}']"),
            (By.XPATH, "//button[contains(text(), '查询')]"),
            (By.XPATH, "//table")
        ]
        
        for by, value in alternative_selectors:
            try:
                element = self._safe_find_element(by, value, 10)
                logging.info(f"备用选择器成功定位元素: {by}={value}")
                return True
            except:
                continue
        
        logging.warning("所有备用选择器均失败")
        return False
    
    def _highlight_and_click(self, element):
        """高亮元素并点击 - 增加稳定性"""
        try:
            self.driver.execute_script("arguments[0].style.border='3px solid green';", element)
            time.sleep(random.uniform(0.8, 2))
            
            # 多种点击方式备用
            try:
                element.click()
            except StaleElementReferenceException:
                self.driver.execute_script("arguments[0].click();", element)
            
            time.sleep(random.uniform(0.8, 2))
        except Exception as e:
            logging.error(f"点击元素时出错: {e}")
            self.driver.execute_script("arguments[0].click();", element)  # 最后尝试JS点击
    
    def _create_date_directory(self, date):
        """创建以日期命名的目录"""
        date_dir = os.path.join(self.save_dir, date)
        if not os.path.exists(date_dir):
            os.makedirs(date_dir, exist_ok=True)
            logging.info(f"创建日期目录: {date_dir}")
        return date_dir
    
    def _save_page_source(self, date, instrument_code, prefix):
        """保存页面源码到日期目录下"""
        date_dir = self._create_date_directory(date)
        filename = f"{date_dir}/{instrument_code}_{prefix}_page.html"
        try:
            with open(filename, 'w', encoding='utf-8') as f:
                f.write(self.driver.page_source)
            logging.info(f"已保存{prefix}页面至: {filename}")
        except Exception as e:
            logging.error(f"保存页面源码失败: {e}")
    
    def find_contract_tables(self, soup):
        """查找有效合约表格 - 优化表格定位"""
        logging.info("开始查找合约表格...")
        contract_tables = []
        
        # 使用更通用的表格选择器，匹配不同类型的表格
        table_selectors = [
            "table.if-table",          # 可能的表格类名1
            "table.IF_if_table",       # 可能的表格类名2
            "table.table",             # 原始选择器
            "table"                    # 最通用的选择器
        ]
        
        for selector in table_selectors:
            for table in soup.select(selector):
                if self.is_valid_position_table(table):
                    title = self.extract_contract_title(soup, table) or "未知合约"
                    contract_tables.append((title, table))
                    logging.info(f"找到有效表格: {title}")
        
        if not contract_tables:
            logging.warning("未找到有效合约表格，尝试备选方法...")
            # 备选方法：查找所有表格并检查是否包含关键文本
            for table in soup.find_all('table'):
                if self.is_valid_position_table(table):
                    title = self.extract_contract_title(soup, table) or "未知合约"
                    contract_tables.append((title, table))
                    logging.info(f"备选方法找到有效表格: {title}")
        
        if not contract_tables:
            logging.warning("未找到任何有效合约表格")
        else:
            logging.info(f"共找到 {len(contract_tables)} 个有效表格")
        
        return contract_tables
    
    def is_valid_position_table(self, table):
        """验证表格有效性 - 更严格的验证逻辑"""
        if not table:
            return False
            
        table_text = table.get_text(strip=True)
        
        # 检查表格是否包含持仓数据的关键列名
        required_columns = ["持买单量", "持卖单量", "会员简称"]
        
        # 额外检查：确保表格中有"合计"行
        has_total_row = "合计" in table_text
        
        # 确保表格包含足够的关键信息
        columns_present = all(col in table_text for col in required_columns)
        
        return columns_present and has_total_row
    
    def extract_contract_title(self, soup, table):
        """增强版合约标题提取方法"""
        # 方法1: 查找包含"合约:"文本的元素
        title_element = table.find_previous(string=lambda t: t and "合约:" in t)
        if title_element:
            parent = title_element.parent
            if parent:
                title = parent.get_text(strip=True)
                return title

        # 方法2: 查找表格前的标题元素
        title_tag = table.find_previous(['h3', 'h4', 'h5', 'div', 'span'], 
                                       class_=lambda c: c and ('title' in str(c) or 'contract' in str(c)))
        if title_tag and title_tag.get_text(strip=True):
            title = title_tag.get_text(strip=True)
            return title

        # 方法3: 查找表格内的标题行
        header_row = table.find('tr', class_=lambda c: c and ('header' in str(c) or 'title' in str(c)))
        if header_row:
            title_cell = header_row.find(['td', 'th'], colspan=lambda c: c and int(c) > 3)
            if title_cell:
                title = title_cell.get_text(strip=True)
                return title

        # 方法4: 使用表格的data-title属性
        if table.has_attr('data-title'):
            title = table['data-title']
            return title

        return None
    
    def extract_position_data(self, table, instrument_code, table_title):
        """解析表格数据（重点提取合计行）- 严格匹配列名版本，精简日志"""
        # 查找合计行
        total_row = None
        for row_idx, row in enumerate(table.find_all('tr')):
            row_text = row.get_text(strip=True)
            if "合计" in row_text:
                total_row = row
                logging.info(f"找到{table_title}的合计行 (行号: {row_idx})")
                break
        if not total_row:
            total_row = table.find_all('tr')[-1] if table.find_all('tr') else None
            if total_row:
                logging.info(f"使用最后一行作为{table_title}的合计行")
            else:
                logging.error(f"{table_title}未找到合计行或表格数据")
                return None, None

        # 查找持买单量和持卖单量的列索引
        long_col_idx, short_col_idx = None, None
        
        # 1. 先在表头行严格匹配列名
        header_row = table.find('tr')
        if header_row:
            long_col_idx, short_col_idx = self._find_columns_in_row(header_row, ["持买单量", "持卖单量"])
        
        # 2. 若未找到则从整个表格的首行开始查找
        if long_col_idx is None or short_col_idx is None:
            for row_idx, row in enumerate(table.find_all('tr')):
                long_idx, short_idx = self._find_columns_in_row(row, ["持买单量", "持卖单量"])
                if long_idx is not None:
                    long_col_idx = long_idx
                if short_idx is not None:
                    short_col_idx = short_idx
                if long_col_idx is not None and short_col_idx is not None:
                    break
        
        # 3. 最终使用默认列位置（第7列和第11列，从0开始计数）
        if long_col_idx is None:
            long_col_idx = 6
            logging.warning(f"使用默认持买单量列位置: {long_col_idx+1}")
        if short_col_idx is None:
            short_col_idx = 10
            logging.warning(f"使用默认持卖单量列位置: {short_col_idx+1}")

        # 提取数据
        cells = total_row.find_all(['td', 'th'])
        
        if len(cells) <= max(long_col_idx, short_col_idx):
            logging.error(f"{table_title}单元格数量不足")
            return None, None
        
        try:
            long_text = cells[long_col_idx].get_text(strip=True).replace(',', '')
            short_text = cells[short_col_idx].get_text(strip=True).replace(',', '')
            
            if not (long_text.isdigit() and short_text.isdigit()):
                logging.error(f"{table_title}数据格式错误: 持买单量={long_text}, 持卖单量={short_text}")
                return None, None
                
            long_pos = int(long_text)
            short_pos = int(short_text)
            net_short = short_pos - long_pos
            logging.info(f"{table_title} - 持买单量: {long_pos}, 持卖单量: {short_pos}, 净空单: {net_short}")
            return long_pos, short_pos
            
        except Exception as e:
            logging.error(f"{table_title}数据解析失败: {e}")
            return None, None

    def _find_columns_in_row(self, row, target_names):
        """在指定行中查找目标列名（严格匹配）"""
        cells = row.find_all(['td', 'th'])
        col_indices = {name: None for name in target_names}
        
        for i, cell in enumerate(cells):
            text = cell.get_text(strip=True)
            if text in target_names and col_indices[text] is None:
                col_indices[text] = i
        
        return col_indices["持买单量"], col_indices["持卖单量"]
    
    def get_instrument_net_short(self, date, instrument_code):
        """获取单个合约的所有表格净空单总和"""
        try:
            # 加载页面
            soup = self.load_dynamic_page(date, instrument_code)
            
            # 查找合约表格
            contract_tables = self.find_contract_tables(soup)
            
            if not contract_tables:
                logging.warning(f"未找到{instrument_code}合约的有效表格")
                return None
            
            # 汇总所有表格的净空单数据
            total_long_pos = 0
            total_short_pos = 0
            valid_tables_count = 0
            
            for contract_title, table in contract_tables:
                long_pos, short_pos = self.extract_position_data(table, instrument_code, contract_title)
                
                if long_pos is not None and short_pos is not None:
                    total_long_pos += long_pos
                    total_short_pos += short_pos
                    valid_tables_count += 1
                    logging.info(f"{contract_title}数据已加入汇总")
                else:
                    logging.warning(f"{contract_title}数据无效，未加入汇总")
            
            if valid_tables_count == 0:
                logging.warning(f"{instrument_code}合约没有有效表格数据")
                return None
            
            total_net_short = total_short_pos - total_long_pos
            logging.info(f"{instrument_code}合约所有表格汇总 - 总持买单量: {total_long_pos}, 总持卖单量: {total_short_pos}, 总净空单: {total_net_short}")
            return total_net_short
            
        except Exception as e:
            logging.error(f"获取{instrument_code}合约数据时出错: {e}")
            return None
    
    def save_results_in_format(self, date, net_short_data):
        """按指定格式保存结果：行为日期，列为各期货品种及总净空单"""
        try:
            # 确保日期格式为YYYYMMDD
            date_str = self.get_trading_date(date)
            
            # 准备数据
            data = {'日期': [date_str]}
            total_net_short = 0
            
            for instrument, name in self.instruments.items():
                # 检查该合约在该日期是否有效
                start_date = self.INSTRUMENT_START_DATES[instrument]
                date_obj = datetime.strptime(date_str, "%Y%m%d")
                
                if instrument in net_short_data and net_short_data[instrument] is not None:
                    # 合约有效期内且数据有效
                    if date_obj >= start_date:
                        data[instrument] = [net_short_data[instrument]]
                        total_net_short += net_short_data[instrument]
                    else:
                        # 合约尚未上市，填充NaN
                        data[instrument] = [np.nan]
                else:
                    # 数据无效，填充NaN
                    data[instrument] = [np.nan]
            
            data['总净空单'] = [total_net_short]
            
            # 创建DataFrame
            df = pd.DataFrame(data)
            
            # 保存路径
            summary_path = f"{self.save_dir}/cffex_net_short_summary.csv"
            
            # 检查文件是否存在
            if os.path.exists(summary_path):
                # 读取现有数据
                existing_df = pd.read_csv(summary_path)
                
                # 确保日期列为字符串
                existing_df['日期'] = existing_df['日期'].astype(str).str.replace(r'\.0$', '', regex=True)
                
                # 检查该日期是否已存在
                if date_str in existing_df['日期'].values:
                    # 日期已存在 - 更新该行数据
                    idx = existing_df.index[existing_df['日期'] == date_str].tolist()[0]
                    
                    # 只更新非NaN的值（即有效合约期内的数据）
                    for col in df.columns:
                        if col != '日期' and not pd.isna(df[col].iloc[0]):
                            existing_df.at[idx, col] = df[col].iloc[0]
                    
                    logging.info(f"更新日期 {date_str} 的数据")
                    updated_df = existing_df
                else:
                    # 日期不存在 - 追加新行
                    updated_df = pd.concat([existing_df, df], ignore_index=True)
                    logging.info(f"添加新日期 {date_str} 的数据")
            else:
                # 文件不存在 - 创建新文件
                updated_df = df
                logging.info(f"创建新数据文件，添加日期 {date_str}")
            
            # 按日期升序排序
            updated_df['日期'] = pd.to_datetime(updated_df['日期'], format='%Y%m%d', errors='coerce')
            updated_df = updated_df.sort_values(by='日期')
            updated_df['日期'] = updated_df['日期'].dt.strftime('%Y%m%d')
            
            # 保存数据
            updated_df.to_csv(summary_path, index=False)
            logging.info(f"数据已按指定格式保存至: {summary_path}")
            
            return True, f"数据已成功保存至 {summary_path}"
            
        except Exception as e:
            logging.error(f"保存数据时出错: {e}")
            return False, f"保存数据失败: {str(e)}"
    
    def is_valid_net_short_data(self, net_short_data):
        """验证净空单数据是否有效（非0）"""
        if net_short_data is None:
            logging.warning("数据无效: net_short_data为None")
            return False
            
        # 检查所有合约数据
        total = 0
        zero_instruments = []
        valid_instruments = []
        
        for instrument, value in net_short_data.items():
            if value is None:
                # 合约未上市是正常的
                logging.info(f"{instrument} 合约未上市")
                continue
                
            if value == 0:
                # 0值表示可能获取失败
                zero_instruments.append(instrument)
            else:
                valid_instruments.append(f"{instrument}:{value}")
                total += value
        
        # 如果有任何合约返回0值
        if zero_instruments:
            logging.warning(f"检测到0值数据: {', '.join(zero_instruments)}")
            if valid_instruments:
                logging.info(f"有效合约数据: {', '.join(valid_instruments)}")
            return False
        
        # 总净空单不能为0
        if total == 0:
            logging.warning("总净空单为0")
            return False
            
        return True
    
    def _process_single_date(self, date):
        """处理单个日期的数据获取（线程安全）"""
        try:
            # 为每个日期创建独立的分析器实例
            analyzer = CFFEXShortPositionAnalyzer(headless=True)
            
            # 获取该日期的所有合约数据
            net_short_data = {}
            for instrument in ["IF", "IC", "IM", "IH"]:
                # 检查该合约在该日期是否已存在
                start_date = self.INSTRUMENT_START_DATES[instrument]
                date_obj = datetime.strptime(date, "%Y%m%d")
                
                if date_obj >= start_date:
                    logging.info(f"获取 {date} 的 {instrument} 合约数据...")
                    net_short = analyzer.get_instrument_net_short(date, instrument)
                    net_short_data[instrument] = net_short
                    if net_short is None:
                        logging.warning(f"{instrument} 合约数据获取失败")
                    elif net_short == 0:
                        logging.warning(f"{instrument} 合约返回0值数据")
                    else:
                        logging.info(f"{instrument} 合约净空单: {net_short}")
                else:
                    net_short_data[instrument] = None
                    logging.info(f"{instrument} 合约在 {date} 尚未上市")
            
            # 验证数据有效性
            if not self.is_valid_net_short_data(net_short_data):
                logging.warning(f"日期 {date} 的数据可能无效，尝试重新获取...")
                
                # 重试机制（最多3次）
                for retry in range(3):
                    logging.warning(f"第{retry+1}次重试获取日期 {date} 的数据...")
                    
                    # 创建新的分析器实例（避免浏览器状态问题）
                    del analyzer
                    analyzer = CFFEXShortPositionAnalyzer(headless=True)
                    
                    # 重新获取数据
                    retry_data = {}
                    for instrument in ["IF", "IC", "IM", "IH"]:
                        if instrument in net_short_data and net_short_data[instrument] is not None:
                            net_short = analyzer.get_instrument_net_short(date, instrument)
                            retry_data[instrument] = net_short
                            if net_short is None:
                                logging.warning(f"重试 {retry+1}: {instrument} 合约数据获取失败")
                            elif net_short == 0:
                                logging.warning(f"重试 {retry+1}: {instrument} 合约返回0值数据")
                            else:
                                logging.info(f"重试 {retry+1}: {instrument} 合约净空单: {net_short}")
                        else:
                            retry_data[instrument] = None
                    
                    # 检查重试数据是否有效
                    if self.is_valid_net_short_data(retry_data):
                        net_short_data = retry_data
                        logging.info(f"重试成功，日期 {date} 的数据已更新")
                        break
                    else:
                        logging.warning(f"重试 {retry+1} 次后数据仍然无效")
            
            # 保存结果
            success, _ = analyzer.save_results_in_format(date, net_short_data)
            
            # 清理资源
            del analyzer
            
            return success
        except Exception as e:
            logging.error(f"处理日期 {date} 时出错: {e}")
            return False
    
    def recheck_zero_values(self, progress_callback=None, thread_count=4):
        """重新检查已有数据中的0值问题 - 仅检查合约有效日期内的数据"""
        logging.info("开始重新检查已有数据中的0值问题（仅有效合约日期内）")
        
        # 获取所有已有数据的日期
        existing_dates, _ = self._get_existing_dates()  # 忽略问题日期，因为已经处理过
        
        # 读取汇总文件
        summary_path = f"{self.save_dir}/cffex_net_short_summary.csv"
        if not os.path.exists(summary_path):
            logging.warning("汇总文件不存在，无法重新检查0值")
            return
        
        # 加载数据
        df = pd.read_csv(summary_path)
        
        # 确保日期列为字符串并去除".0"后缀
        df['日期'] = df['日期'].astype(str).str.replace(r'\.0$', '', regex=True)
        
        # 找出有0值问题的日期
        problem_dates = []
        for idx, row in df.iterrows():
            date = str(row['日期'])
            
            # 确保日期格式正确
            if len(date) == 8:
                formatted_date = date
            else:
                # 尝试解析日期
                try:
                    # 处理可能包含非数字字符的情况
                    date_clean = ''.join(filter(str.isdigit, date))
                    if len(date_clean) == 8:
                        formatted_date = date_clean
                    else:
                        logging.warning(f"无法解析日期格式: {date}")
                        continue
                except:
                    logging.warning(f"无法解析日期格式: {date}")
                    continue
            
            # 检查日期对象
            try:
                date_obj = datetime.strptime(formatted_date, "%Y%m%d")
            except:
                logging.warning(f"无效日期格式: {formatted_date}")
                continue
            
            # 检查各合约值是否有0值问题（仅限合约有效日期内）
            zero_instruments = []
            instrument_values = {}
            instrument_status = {}
            
            for instrument in ["IF", "IC", "IM", "IH"]:
                value = row.get(instrument, None)
                instrument_values[instrument] = value
                
                # 检查该合约在该日期是否有效
                start_date = self.INSTRUMENT_START_DATES[instrument]
                is_valid = date_obj >= start_date
                instrument_status[instrument] = "有效期内" if is_valid else "未上市"
                
                # 如果合约值存在且为0，并且该合约在该日期有效
                if is_valid and value is not None and value == 0:
                    zero_instruments.append(instrument)
            
            # 如果有任何有效合约值为0
            if zero_instruments:
                problem_dates.append(formatted_date)
                # 记录详细数据
                values_info = []
                for instrument in ["IF", "IC", "IM", "IH"]:
                    status = instrument_status[instrument]
                    value = instrument_values[instrument]
                    if instrument in zero_instruments:
                        # 高亮显示0值合约
                        values_info.append(f"{instrument}: {value} (0值问题! {status})")
                    else:
                        values_info.append(f"{instrument}: {value} ({status})")
                
                values_str = "\n  ".join(values_info)
                
                logging.warning(f"检测到0值问题日期: {formatted_date}")
                logging.warning(f"  各合约情况:")
                logging.warning(f"  {values_str}")
                logging.warning(f"  0值合约: {', '.join(zero_instruments)}")
        
        if not problem_dates:
            logging.info("未检测到有0值问题的日期")
            return
        
        logging.warning(f"发现 {len(problem_dates)} 个有0值问题的日期，将重新获取数据")
        logging.warning(f"问题日期列表: {', '.join(problem_dates)}")
        
        # 创建线程池重新获取这些日期的数据
        results = []
        completed_count = 0
        start_time = time.time()
        total_problems = len(problem_dates)
        
        # 如果有进度回调，初始化进度
        if progress_callback:
            progress_callback(0, total_problems, 0)
        
        with ThreadPoolExecutor(max_workers=thread_count) as executor:
            # 提交所有任务
            future_to_date = {executor.submit(self._process_single_date, date): date for date in problem_dates}
            
            # 处理完成的任务
            for future in as_completed(future_to_date):
                date = future_to_date[future]
                try:
                    result = future.result()
                    results.append(result)
                    completed_count += 1
                    
                    # 更新进度
                    elapsed_time = time.time() - start_time
                    remaining_time = (elapsed_time / completed_count) * (total_problems - completed_count) if completed_count > 0 else 0
                    
                    if progress_callback:
                        progress_callback(completed_count, total_problems, remaining_time)
                    
                    logging.info(f"完成重新获取日期 {date} ({completed_count}/{total_problems})")
                except Exception as e:
                    logging.error(f"重新获取日期 {date} 时出错: {e}")
        
        # 统计结果
        success_count = sum(1 for r in results if r)
        failure_count = total_problems - success_count
        
        message = f"0值问题重新获取完成: {success_count} 天成功, {failure_count} 天失败"
        logging.info(message)
        return True, message
    
    def get_all_historical_data(self, progress_callback=None, thread_count=4, force_recheck=False):
        """
        获取所有历史数据（从合约最早日期到今天）
        
        参数:
            progress_callback: 进度回调函数
            thread_count: 使用的线程数量
            force_recheck: 强制重新检查已有数据（即使存在）
        """
        logging.info(f"开始获取所有历史数据，使用 {thread_count} 个线程")
        
        # 获取所有需要查询的日期
        all_dates = self._get_all_historical_dates()
        total_dates = len(all_dates)
        
        if total_dates == 0:
            logging.info("没有需要获取的历史数据")
            # 即使没有新数据，也需要检查0值问题
            self.recheck_zero_values(progress_callback, thread_count)
            return True, "没有需要获取的历史数据，已执行0值检查"
        
        # 获取已有日期和问题日期
        existing_dates, problem_dates = self._get_existing_dates()
        
        # 如果有问题日期，添加到需要获取的日期中
        dates_to_fetch = []
        if force_recheck:
            logging.info("强制重新检查所有日期")
            dates_to_fetch = all_dates
        else:
            # 添加缺失日期
            dates_to_fetch = [date for date in all_dates if date not in existing_dates]
            # 添加问题日期（0值重复行）
            dates_to_fetch.extend(problem_dates)
            # 去重
            dates_to_fetch = list(set(dates_to_fetch))
        
        # 记录需要获取的日期
        logging.info(f"需要获取的历史数据天数: {len(dates_to_fetch)}")
        if problem_dates:
            logging.warning(f"其中包括 {len(problem_dates)} 个有0值问题的重复日期")
        
        if not dates_to_fetch:
            logging.info("没有需要获取的日期")
            # 即使没有新数据，也需要检查0值问题
            self.recheck_zero_values(progress_callback, thread_count)
            return True, "没有需要获取的日期，已执行0值检查"
        
        # 创建线程池
        results = []
        completed_count = 0
        start_time = time.time()
        
        # 初始化进度
        if progress_callback:
            progress_callback(0, len(dates_to_fetch), 0)
        
        with ThreadPoolExecutor(max_workers=thread_count) as executor:
            # 提交所有任务
            future_to_date = {executor.submit(self._process_single_date, date): date for date in dates_to_fetch}
            
            # 处理完成的任务
            for future in as_completed(future_to_date):
                date = future_to_date[future]
                try:
                    result = future.result()
                    results.append(result)
                    completed_count += 1
                    
                    # 更新进度
                    elapsed_time = time.time() - start_time
                    remaining_time = (elapsed_time / completed_count) * (len(dates_to_fetch) - completed_count) if completed_count > 0 else 0
                    
                    if progress_callback:
                        progress_callback(completed_count, len(dates_to_fetch), remaining_time)
                    
                    logging.info(f"完成日期 {date} ({completed_count}/{len(dates_to_fetch)})")
                except Exception as e:
                    logging.error(f"处理日期 {date} 时出错: {e}")
        
        # 统计结果
        success_count = sum(1 for r in results if r)
        failure_count = len(dates_to_fetch) - success_count
        
        message = f"历史数据获取完成: {success_count} 天成功, {failure_count} 天失败"
        logging.info(message)
        
        # 无论是否有失败日期，都执行0值检查
        self.recheck_zero_values(progress_callback, thread_count)
        
        return True, message
    
    def _get_all_historical_dates(self):
        """获取所有需要查询的历史日期"""
        # 获取所有合约的最早日期
        earliest_date = min(self.INSTRUMENT_START_DATES.values())
        today = datetime.now()
        
        # 获取所有交易日
        all_trading_days = self._get_trading_days_between(earliest_date, today)
        
        # 过滤掉已有数据的日期
        existing_dates = self._get_existing_dates()
        dates_to_fetch = [date for date in all_trading_days if date not in existing_dates]
        
        # 记录需要获取的日期
        logging.info(f"需要获取的历史数据天数: {len(dates_to_fetch)} (最早日期: {earliest_date.strftime('%Y%m%d')})")
        if dates_to_fetch:
            # 打印前5个和后5个日期作为示例
            sample_size = min(10, len(dates_to_fetch))
            if len(dates_to_fetch) > 10:
                sample_dates = dates_to_fetch[:5] + ["..."] + dates_to_fetch[-5:]
            else:
                sample_dates = dates_to_fetch
            logging.info(f"缺漏日期示例: {', '.join(sample_dates)}")
        else:
            logging.info("没有需要获取的日期")
        
        return dates_to_fetch
    
    def _get_trading_days_between(self, start_date, end_date):
        """获取两个日期之间的所有交易日"""
        schedule = self.calendar.schedule(
             start_date=start_date.strftime("%Y-%m-%d"),
             end_date=end_date.strftime("%Y-%m-%d")
         )
        
        trading_days = [pd.Timestamp(date).strftime("%Y%m%d") for date in schedule.index]
        return trading_days
    
    def _get_existing_dates(self):
        """获取已存在数据的日期列表 - 处理重复键值并移除0值问题行"""
        existing_dates = set()
        duplicate_dates = set()  # 用于存储检测到的重复日期
        date_data = {}  # 存储每个日期的完整数据行
        problem_dates = set()  # 存储有0值问题的日期
        
        logging.info("开始检查已存在数据日期，并处理重复键值和0值问题...")
        
        # 1. 检查CSV汇总文件
        summary_path = f"{self.save_dir}/cffex_net_short_summary.csv"
        if os.path.exists(summary_path):
            try:
                logging.info(f"检查汇总文件: {summary_path}")
                # 读取整个文件
                df = pd.read_csv(summary_path)
                
                # 确保日期列为字符串并去除".0"后缀
                df['日期'] = df['日期'].astype(str).str.replace(r'\.0$', '', regex=True)
                
                # 检测重复日期
                duplicate_rows = df[df.duplicated(subset=['日期'], keep=False)]
                if not duplicate_rows.empty:
                    logging.warning(f"发现 {len(duplicate_rows)} 行重复日期数据")
                    
                    # 处理重复日期
                    for date in duplicate_rows['日期'].unique():
                        date_str = str(date)
                        duplicates = df[df['日期'] == date]
                        
                        # 检查是否有0值问题（仅限合约期内）
                        rows_to_delete = []
                        
                        for idx, row in duplicates.iterrows():
                            # 解析日期
                            try:
                                # 处理可能包含非数字字符的情况
                                date_clean = ''.join(filter(str.isdigit, date_str))
                                if len(date_clean) == 8:
                                    date_obj = datetime.strptime(date_clean, "%Y%m%d")
                                else:
                                    logging.warning(f"无法解析日期格式: {date_str}")
                                    continue
                                
                                # 检查该行是否有合约期内的0值
                                has_valid_zero = False
                                for instrument in ["IF", "IC", "IM", "IH"]:
                                    start_date = self.INSTRUMENT_START_DATES[instrument]
                                    if date_obj >= start_date:
                                        value = row.get(instrument)
                                        if value == 0:
                                            has_valid_zero = True
                                            logging.info(f"日期 {date_str} 行 {idx} 检测到合约期内0值: {instrument}")
                                            break
                                
                                if has_valid_zero:
                                    rows_to_delete.append(idx)
                                    logging.info(f"标记日期 {date_str} 行 {idx} 待删除 (合约期内0值)")
                            except:
                                continue
                        
                        if rows_to_delete:
                            # 删除标记的行
                            df = df.drop(rows_to_delete)
                            problem_dates.add(date_str)
                            logging.info(f"日期 {date_str} 已删除 {len(rows_to_delete)} 行有合约期内0值的记录")
                        else:
                            # 没有合约期内0值，保留最后一行
                            last_row = duplicates.iloc[-1]
                            df = df[df['日期'] != date]
                            df = pd.concat([df, pd.DataFrame([last_row])], ignore_index=True)
                            logging.info(f"日期 {date_str} 的重复行已处理，保留最后一行")
                
                # 保存处理后的文件（去重后）
                df.to_csv(summary_path, index=False)
                logging.info("重复日期处理完成，文件已更新")
                
                # 现在处理有效日期
                if '日期' in df.columns:
                    # 尝试多种日期格式解析
                    for _, row in df.iterrows():
                        date_str = str(row['日期'])
                        
                        # 清理日期字符串
                        date_clean = ''.join(filter(str.isdigit, date_str))
                        if len(date_clean) == 8:
                            existing_dates.add(date_clean)
                        else:
                            logging.warning(f"无法解析日期格式: {date_str}")
                    
                    logging.info(f"从汇总文件添加了 {len(df)} 个日期")
                else:
                    logging.warning("汇总文件中缺少'日期'列")
            except Exception as e:
                logging.error(f"读取汇总文件失败: {e}")
        else:
            logging.info("汇总文件不存在")
        
        # 记录找到的已有日期
        if existing_dates:
            sorted_dates = sorted(existing_dates)
            total_dates = len(sorted_dates)
            
            # 详细日志输出
            logging.info(f"总共存在 {total_dates} 个唯一日期数据")
            logging.info(f"检测到 {len(duplicate_dates)} 个重复日期")
            
            # 输出问题日期
            if problem_dates:
                logging.warning(f"检测到 {len(problem_dates)} 个有0值问题的重复日期，需要重新获取:")
                for date in sorted(problem_dates):
                    logging.warning(f"  {date}")
            
            # 输出日期示例
            if total_dates > 10:
                sample = sorted_dates[:5] + ["..."] + sorted_dates[-5:]
                sample_str = ", ".join(sample)
            else:
                sample_str = ", ".join(sorted_dates)
            
            logging.info(f"日期示例: {sample_str}")
        else:
            logging.info("未找到任何已有日期数据")
        
        # 返回问题日期，这些日期需要重新获取
        return existing_dates, problem_dates

    def verify_data_file(self):
        """
        验证数据文件：
        1. 检测重复日期键值
        2. 检查各自合约期内是否有缺漏交易日日期
        3. 检测0值问题行（仅限合约期内）
        """
        summary_path = f"{self.save_dir}/cffex_net_short_summary.csv"
        if not os.path.exists(summary_path):
            logging.error("汇总文件不存在，无法验证")
            return False, "汇总文件不存在", []
        
        try:
            df = pd.read_csv(summary_path)
            
            # 确保日期列为字符串并去除".0"后缀
            df['日期'] = df['日期'].astype(str).str.replace(r'\.0$', '', regex=True)
            
            report = "数据文件验证报告:\n"
            report += "=" * 50 + "\n"
            
            # 1. 检测重复日期
            duplicate_report, df = self._detect_duplicate_dates(df)
            report += duplicate_report
            
            # 2. 检查缺漏交易日
            missing_report = self._detect_missing_dates(df)
            report += missing_report
            
            # 3. 检测0值问题行（仅限合约期内）
            zero_report, zero_dates = self._detect_zero_value_rows(df)
            report += zero_report
            
            report += "=" * 50
            
            # 保存处理后的文件
            df.to_csv(summary_path, index=False)
            logging.info("文件已更新")
            
            logging.info(report)
            return True, report, zero_dates
            
        except Exception as e:
            error_msg = f"验证数据文件时出错: {str(e)}"
            logging.error(error_msg)
            return False, error_msg, []
    
    def _detect_duplicate_dates(self, df):
        """检测重复日期并处理 - 精确检查合约期内的0值"""
        report = "\n[重复日期检测]\n"
        deleted_rows = 0
        
        # 检查日期列是否存在
        if '日期' not in df.columns:
            report += "错误: 数据文件中缺少'日期'列\n"
            return report, df
        
        # 确保日期列为字符串
        df['日期'] = df['日期'].astype(str)
        
        # 查找重复日期
        duplicates = df[df.duplicated(subset=['日期'], keep=False)]
        if duplicates.empty:
            report += "未检测到重复日期\n"
            return report, df
        
        # 统计重复日期
        duplicate_dates = duplicates['日期'].unique()
        report += f"检测到 {len(duplicate_dates)} 个重复日期:\n"
        
        for date in duplicate_dates:
            date_str = str(date)
            dup_rows = df[df['日期'] == date]
            report += f"日期 {date_str} 有 {len(dup_rows)} 行重复数据\n"
            
            # 检查日期格式
            try:
                # 清理日期字符串
                date_clean = ''.join(filter(str.isdigit, date_str))
                if len(date_clean) == 8:
                    date_obj = datetime.strptime(date_clean, "%Y%m%d")
                else:
                    report += f"  警告: 无法解析日期格式: {date_str}\n"
                    continue
            except Exception as e:
                report += f"  警告: 解析日期失败: {date_str} - {str(e)}\n"
                continue
            
            # 检查每一行是否有合约期内的0值
            rows_to_delete = []
            for idx, row in dup_rows.iterrows():
                has_valid_zero = False
                problem_instruments = []
                
                for instrument in ["IF", "IC", "IM", "IH"]:
                    start_date = self.INSTRUMENT_START_DATES[instrument]
                    
                    # 检查该合约在该日期是否有效
                    if date_obj >= start_date:
                        value = row.get(instrument)
                        if value == 0:
                            has_valid_zero = True
                            problem_instruments.append(instrument)
                
                if has_valid_zero:
                    rows_to_delete.append(idx)
                    report += f"  行 {idx}: 检测到合约期内0值 ({', '.join(problem_instruments)}), 标记为删除\n"
                else:
                    report += f"  行 {idx}: 无合约期内0值问题\n"
            
            if rows_to_delete:
                # 删除有问题的行
                df = df.drop(rows_to_delete)
                deleted_rows += len(rows_to_delete)
                report += f"  已删除 {len(rows_to_delete)} 行有合约期内0值的记录\n"
                
                # 如果删除后仍然有重复，保留最后一行
                remaining = df[df['日期'] == date]
                if len(remaining) > 1:
                    last_row = remaining.iloc[-1]
                    df = df[df['日期'] != date]
                    df = pd.concat([df, pd.DataFrame([last_row])], ignore_index=True)
                    report += f"  删除后仍有重复，保留最后一行\n"
            else:
                # 没有合约期内0值问题，保留最后一行
                last_row = dup_rows.iloc[-1]
                df = df[df['日期'] != date]
                df = pd.concat([df, pd.DataFrame([last_row])], ignore_index=True)
                report += f"  无合约期内0值问题，保留最后一行\n"
        
        report += f"重复日期处理完成，共删除 {deleted_rows} 行有问题的记录\n"
        return report, df
    
    def _detect_missing_dates(self, df):
        """检测合约期内缺漏的交易日日期"""
        report = "\n[缺漏交易日检测]\n"
        
        # 检查日期列是否存在
        if '日期' not in df.columns:
            report += "错误: 数据文件中缺少'日期'列\n"
            return report
        
        # 确保日期列为字符串
        df['日期'] = df['日期'].astype(str)
        
        # 获取所有已有日期
        existing_dates = set()
        for date in df['日期']:
            try:
                # 清理日期字符串
                date_clean = ''.join(filter(str.isdigit, str(date)))
                if len(date_clean) == 8:
                    existing_dates.add(date_clean)
            except:
                continue
        
        # 检查各合约的缺漏日期
        missing_dates_by_contract = {}
        total_missing = 0
        
        for instrument, start_date in self.INSTRUMENT_START_DATES.items():
            # 获取合约有效日期范围
            today = datetime.now()
            if today < start_date:
                continue  # 合约尚未开始
                
            # 获取该合约的所有交易日
            trading_days = self._get_trading_days_between(start_date, today)
            
            # 查找缺失日期
            missing_dates = [d for d in trading_days if d not in existing_dates]
            
            if missing_dates:
                missing_dates_by_contract[instrument] = missing_dates
                total_missing += len(missing_dates)
                report += f"{instrument} 合约缺漏 {len(missing_dates)} 个交易日\n"
        
        if total_missing == 0:
            report += "未检测到缺漏交易日\n"
        else:
            report += f"\n总缺漏交易日数: {total_missing}\n"
            
            # 显示部分缺漏日期作为示例
            sample_report = "缺漏日期示例:\n"
            for instrument, dates in missing_dates_by_contract.items():
                sample_size = min(5, len(dates))
                sample_dates = dates[:sample_size]
                sample_report += f"  {instrument}: {', '.join(sample_dates)}"
                if len(dates) > sample_size:
                    sample_report += f", ... (共{len(dates)}天)"
                sample_report += "\n"
            
            report += sample_report
        
        return report

    def _detect_zero_value_rows(self, df):
        """检测有0值问题的行（仅限合约期内）"""
        report = "\n[0值问题检测（仅限合约期内）]\n"
        zero_dates = []
        
        # 检查日期列是否存在
        if '日期' not in df.columns:
            report += "错误: 数据文件中缺少'日期'列\n"
            return report, zero_dates
        
        # 确保日期列为字符串
        df['日期'] = df['日期'].astype(str)
        
        # 遍历所有行
        for idx, row in df.iterrows():
            date_val = row['日期']
            # 转换日期
            try:
                # 清理日期字符串
                date_clean = ''.join(filter(str.isdigit, str(date_val)))
                if len(date_clean) == 8:
                    date_obj = datetime.strptime(date_clean, "%Y%m%d")
                else:
                    continue
            except:
                continue
            
            zero_instruments = []
            for instrument in ["IF", "IC", "IM", "IH"]:
                # 检查该合约在该日期是否已经上市
                start_date = self.INSTRUMENT_START_DATES[instrument]
                if date_obj >= start_date:
                    value = row.get(instrument, None)
                    if value is not None and value == 0:
                        zero_instruments.append(instrument)
            
            # 如果有0值问题
            if zero_instruments:
                zero_dates.append(date_val)
                report += f"日期 {date_val} 检测到0值合约: {', '.join(zero_instruments)}\n"
        
        if not zero_dates:
            report += "未检测到有0值问题的行（合约期内）\n"
        else:
            report += f"总共检测到 {len(zero_dates)} 个有0值问题的日期（合约期内）\n"
        
        return report, zero_dates

    def __del__(self):
        """确保浏览器资源被释放"""
        try:
            if hasattr(self, 'driver') and self.driver:
                self.driver.quit()
                logging.info("WebDriver已安全关闭")
        except Exception as e:
            logging.warning(f"关闭WebDriver时出错: {e}")


class CFFEXGUIApp:
    """净空单数据获取工具的GUI应用 - 完整支持中文显示"""
    
    def __init__(self, root):
        self.root = root
        self.root.title("中国金融期货交易所净空单数据获取工具")
        self.root.geometry("1200x800")
        self.root.resizable(True, True)
        
        # 设置全局字体
        # self.set_global_font()
        
        self.analyzer = None
        self.is_running = False
        self.historical_thread = None
        self.progress_window = None
        self.data_mode = tk.StringVar(value="days")
        
        self.create_widgets()


    def log_message(self, message):
        """在日志区域显示消息"""
        self.log_text.config(state=tk.NORMAL)
        self.log_text.insert(tk.END, message + "\n")
        self.log_text.see(tk.END)
        self.log_text.config(state=tk.DISABLED)
        logging.info(message)
    
    def clear_log(self):
        """清空日志区域"""
        self.log_text.config(state=tk.NORMAL)
        self.log_text.delete(1.0, tk.END)
        self.log_text.config(state=tk.DISABLED)
    
    def analysis_complete(self):
        """分析完成后的清理工作"""
        self.is_running = False
        self.historical_button.config(state=tk.NORMAL)
        self.status_var.set("准备就绪")
        self.log_message("数据获取完成")
    
    def start_historical_analysis(self):
        """开始获取所有历史数据"""
        if self.is_running:
            messagebox.showwarning("警告", "数据获取正在进行中，请稍候...")
            return
        
        # 询问线程数量
        thread_count = simpledialog.askinteger(
            "线程数量", 
            "请输入要使用的线程数量 (推荐4-8):", 
            parent=self.root,
            minvalue=1, 
            maxvalue=16,
            initialvalue=4
        )
        
        if not thread_count:
            return
        
        # 询问是否强制重新获取
        force_recheck = messagebox.askyesno(
            "强制重新获取",
            "是否强制重新获取所有数据？\n\n选择'是'将忽略已有数据重新获取所有日期\n选择'否'只获取缺失数据",
            parent=self.root
        )
        
        self.is_running = True
        self.historical_button.config(state=tk.DISABLED)
        self.status_var.set("历史数据获取中...")
        self.log_message(f"开始获取所有历史数据，使用 {thread_count} 个线程...")
        
        # 创建进度窗口
        self.create_progress_window()
        
        # 在新线程中执行历史数据获取
        self.historical_thread = threading.Thread(
            target=self.run_historical_analysis_in_thread, 
            args=(thread_count, force_recheck),
            daemon=True
        )
        self.historical_thread.start()
    
    def create_progress_window(self):
        """创建进度显示窗口 - 中文支持"""
        if self.progress_window and self.progress_window.winfo_exists():
            self.progress_window.destroy()
        
        self.progress_window = tk.Toplevel(self.root)
        self.progress_window.title("历史数据获取进度")
        self.progress_window.geometry("400x200")
        self.progress_window.resizable(False, False)
        self.progress_window.protocol("WM_DELETE_WINDOW", lambda: None)  # 禁用关闭按钮
        
        # 进度标签
        ttk.Label(self.progress_window, text="历史数据获取进度:", font=("Arial", 12)).pack(pady=10)
        
        # 进度条
        self.progress_var = tk.DoubleVar()
        self.progress_bar = ttk.Progressbar(
            self.progress_window, 
            variable=self.progress_var, 
            maximum=100, 
            length=300
        )
        self.progress_bar.pack(pady=10)
        
        # 进度文本
        self.progress_text = tk.StringVar()
        self.progress_text.set("准备开始...")
        ttk.Label(self.progress_window, textvariable=self.progress_text).pack(pady=5)
        
        # 剩余时间
        self.time_text = tk.StringVar()
        self.time_text.set("预计剩余时间: 计算中...")
        ttk.Label(self.progress_window, textvariable=self.time_text).pack(pady=5)
        
        # 取消按钮
        self.cancel_button = ttk.Button(
            self.progress_window, 
            text="取消获取", 
            command=self.cancel_historical_analysis
        )
        self.cancel_button.pack(pady=10)
        
        # 居中窗口
        self.progress_window.update_idletasks()
        width = self.progress_window.winfo_width()
        height = self.progress_window.winfo_height()
        x = (self.root.winfo_width() - width) // 2 + self.root.winfo_x()
        y = (self.root.winfo_height() - height) // 2 + self.root.winfo_y()
        self.progress_window.geometry(f"+{x}+{y}")
    
    def cancel_historical_analysis(self):
        """取消历史数据获取"""
        if self.is_running:
            if messagebox.askyesno("确认", "确定要取消历史数据获取吗？"):
                self.is_running = False
                self.status_var.set("操作已取消")
                self.log_message("用户取消了历史数据获取")
                
                if self.progress_window:
                    self.progress_window.destroy()
                    self.progress_window = None
                
                self.analysis_complete()
    
    def update_progress(self, completed, total, remaining_time):
        """更新进度显示"""
        if not self.is_running:
            return
            
        # 计算进度百分比
        percent = (completed / total) * 100 if total > 0 else 0
        
        # 更新进度条
        self.progress_var.set(percent)
        
        # 更新进度文本
        self.progress_text.set(f"已完成: {completed}/{total} ({percent:.1f}%)")
        
        # 更新剩余时间
        if remaining_time > 0:
            hours, remainder = divmod(remaining_time, 3600)
            minutes, seconds = divmod(remainder, 60)
            time_str = f"{int(hours):02d}:{int(minutes):02d}:{int(seconds):02d}"
            self.time_text.set(f"预计剩余时间: {time_str}")
        else:
            self.time_text.set("预计剩余时间: 计算中...")
        
        # 强制更新UI
        self.progress_window.update()
    
    def run_historical_analysis_in_thread(self, thread_count, force_recheck):
        """在新线程中执行历史数据获取"""
        try:
            # 初始化分析器
            self.analyzer = CFFEXShortPositionAnalyzer(headless=True)
            self.log_message("历史数据获取分析器初始化完成")
            
            # 获取所有历史数据
            success, message = self.analyzer.get_all_historical_data(
                progress_callback=self.update_progress,
                thread_count=thread_count,
                force_recheck=force_recheck
            )
            
            self.log_message(message)
            
            if success:
                self.root.after(0, lambda: messagebox.showinfo("成功", "历史数据获取完成"))
            else:
                self.root.after(0, lambda: messagebox.showerror("错误", message))
            
        except Exception as e:
            error_msg = f"历史数据获取过程中出错: {str(e)}"
            self.log_message(error_msg)
            self.root.after(0, lambda: messagebox.showerror("错误", error_msg))
        finally:
            # 清理资源
            if self.analyzer:
                del self.analyzer
                self.analyzer = None
                
            # 关闭进度窗口
            if self.progress_window:
                self.root.after(0, self.progress_window.destroy)
                self.progress_window = None
                
            # 更新UI状态
            self.root.after(0, self.analysis_complete)

    def verify_data_file(self):
        """验证数据文件完整性"""
        if self.is_running:
            messagebox.showwarning("警告", "请等待当前操作完成")
            return
        
        try:
            self.status_var.set("数据文件验证中...")
            self.log_message("开始验证数据文件完整性...")
            
            # 初始化分析器
            self.analyzer = CFFEXShortPositionAnalyzer(headless=True)
            
            # 执行验证
            success, report, zero_dates = self.analyzer.verify_data_file()
            
            if success:
                self.log_message("数据文件验证完成")
                
                # 显示验证报告对话框
                report_window = tk.Toplevel(self.root)
                report_window.title("数据文件验证报告")
                report_window.geometry("800x600")
                
                # 报告文本区域
                text_frame = ttk.Frame(report_window, padding="10")
                text_frame.pack(fill=tk.BOTH, expand=True)
                
                report_text = scrolledtext.ScrolledText(
                    text_frame, 
                    wrap=tk.WORD, 
                    font=("Courier New", 10)
                )
                report_text.pack(fill=tk.BOTH, expand=True)
                report_text.insert(tk.END, report)
                report_text.config(state=tk.DISABLED)
                
                # 按钮框架
                btn_frame = ttk.Frame(report_window, padding="10")
                btn_frame.pack(fill=tk.X)
                
                # 关闭按钮
                close_btn = ttk.Button(
                    btn_frame, 
                    text="关闭", 
                    command=report_window.destroy
                )
                close_btn.pack(side=tk.RIGHT, padx=10)
                
                # 删除0值行按钮（如果有0值问题）
                if zero_dates:
                    del_btn = ttk.Button(
                        btn_frame, 
                        text="删除所有0值问题行", 
                        command=lambda: self.delete_zero_value_rows(zero_dates, report_window)
                    )
                    del_btn.pack(side=tk.LEFT, padx=10)
                
                # 获取缺失数据按钮（如果有缺漏）
                if "缺漏" in report:
                    fetch_btn = ttk.Button(
                        btn_frame, 
                        text="获取缺失数据", 
                        command=self.fetch_missing_data
                    )
                    fetch_btn.pack(side=tk.LEFT, padx=10)
                
                self.status_var.set("数据文件验证完成")
            else:
                self.log_message(f"验证失败: {report}")
                messagebox.showerror("验证失败", report)
                self.status_var.set("验证失败")
                
        except Exception as e:
            error_msg = f"验证数据文件时出错: {str(e)}"
            self.log_message(error_msg)
            messagebox.showerror("错误", error_msg)
            self.status_var.set("验证出错")
        finally:
            # 清理资源
            if self.analyzer:
                del self.analyzer
                self.analyzer = None
    
    def delete_zero_value_rows(self, zero_dates, report_window):
        """删除所有有0值问题的行"""
        if not zero_dates:
            messagebox.showinfo("提示", "没有需要删除的0值问题行")
            return
        
        # 确认删除
        if not messagebox.askyesno("确认删除", f"确定要删除 {len(zero_dates)} 个有0值问题的日期行吗？"):
            return
        
        try:
            # 重新初始化分析器
            self.analyzer = CFFEXShortPositionAnalyzer(headless=True)
            
            # 加载数据文件
            summary_path = f"{self.analyzer.save_dir}/cffex_net_short_summary.csv"
            df = pd.read_csv(summary_path)
            
            # 删除所有有0值问题的行
            original_count = len(df)
            df = df[~df['日期'].isin(zero_dates)]
            deleted_count = original_count - len(df)
            
            # 保存文件
            df.to_csv(summary_path, index=False)
            
            # 关闭报告窗口
            report_window.destroy()
            
            # 显示结果
            messagebox.showinfo("删除完成", f"已成功删除 {deleted_count} 个有0值问题的日期行")
            self.log_message(f"已删除 {deleted_count} 个有0值问题的日期行")
            
        except Exception as e:
            error_msg = f"删除0值问题行时出错: {str(e)}"
            self.log_message(error_msg)
            messagebox.showerror("错误", error_msg)
        finally:
            if self.analyzer:
                del self.analyzer
                self.analyzer = None

    def fetch_missing_data(self):
        """获取缺失数据"""
        # 询问线程数量
        thread_count = simpledialog.askinteger(
            "线程数量", 
            "请输入要使用的线程数量 (推荐4-8):", 
            parent=self.root,
            minvalue=1, 
            maxvalue=16,
            initialvalue=4
        )
        
        if not thread_count:
            return
        
        self.is_running = True
        self.status_var.set("获取缺失数据中...")
        self.log_message(f"开始获取缺失数据，使用 {thread_count} 个线程...")
        
        # 创建进度窗口
        self.create_progress_window()
        
        # 在新线程中执行历史数据获取
        self.historical_thread = threading.Thread(
            target=self.run_historical_analysis_in_thread, 
            args=(thread_count, True),  # force_recheck=True 确保获取缺失数据
            daemon=True
        )
        self.historical_thread.start()

    def on_closing(self):
        """应用关闭时的处理"""
        if self.is_running:
            if not messagebox.askokcancel("退出", "数据获取正在进行中，确定要退出吗？"):
                return
        
        if self.progress_window:
            self.progress_window.destroy()
        
        if messagebox.askokcancel("退出", "确定要退出应用吗?"):
            if self.analyzer:
                del self.analyzer
            self.root.destroy()

    def create_widgets(self):
        """创建GUI组件 - 全部使用中文标签"""
        # 顶部框架 - 日期输入
        top_frame = ttk.Frame(self.root, padding="10")
        top_frame.pack(fill=tk.X)
       
        self.status_var = tk.StringVar()
        self.status_var.set("准备就绪")
        ttk.Label(top_frame, textvariable=self.status_var).pack(side=tk.RIGHT, padx=10)
        
        # 中间框架 - 操作按钮
        button_frame = ttk.Frame(self.root, padding="10")
        button_frame.pack(fill=tk.X)
        
        self.historical_button = ttk.Button(button_frame, text="获取所有历史数据", command=self.start_historical_analysis)
        self.historical_button.pack(side=tk.LEFT, padx=10)
        
        self.clear_button = ttk.Button(button_frame, text="清空日志", command=self.clear_log)
        self.clear_button.pack(side=tk.LEFT, padx=10)
        
        # 添加验证按钮
        self.verify_button = ttk.Button(button_frame, text="验证数据文件", command=self.verify_data_file)
        self.verify_button.pack(side=tk.LEFT, padx=10)        
        
        # 底部框架 - 日志显示
        log_frame = ttk.LabelFrame(self.root, text="操作日志", padding="10")
        log_frame.pack(fill=tk.BOTH, expand=True)
        
        ttk.Label(log_frame, text="操作日志:").pack(anchor=tk.W)
        
        self.log_text = scrolledtext.ScrolledText(log_frame, wrap=tk.WORD, height=20)
        self.log_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        self.log_text.config(state=tk.DISABLED)
        
        # 绑定关闭事件
        self.root.protocol("WM_DELETE_WINDOW", self.on_closing)


# 主程序入口
if __name__ == "__main__":
    root = tk.Tk()
    app = CFFEXGUIApp(root)
    root.mainloop()
